;
; FP and LONG functions for floating point and long packages
;

	INCLUDE "bds.lib"


	FUNCTION	fp

	CALL	arghak
	PUSH	B		; save BC
 	LXI	H,COMMON$EXIT	 

	PUSH	H		; save the common exit addr in the stack
	LDA	arg1		;Get code ptr
	RAL			;Multiply code by 2
	MOV	E,A
	MVI	D,0		;Move result to DE
	LXI	H,JMPTAB	;Get JMPTAB addr
	DAD	D		;Add offset to it
	XCHG			;Store result in DE
	LDAX	D
	MOV	L,A
	INX	D
	LDAX	D
	MOV	H,A		;Move table addr to HL
	PCHL			;Jump to selected routine
JMPTAB:
	DW	XNORM
	DW	XADD
	DW	XSUB
	DW	XMULT
	DW	XDIV
	DW	XFTOA

COMMON$EXIT:
	POP	B		; restore BC
	RET			; return to BDS C

XNORM:
	CALL	LD$OP1
	CALL	FPNORM
EXIT0:
	CALL	ST$ACC
	RET

XADD:
	CALL	LD$OP2
	CALL	FPADD
	JMP	EXIT0
XSUB:
	CALL	LD$OP2
	CALL	FPSUB
	JMP	EXIT0
XMULT:
	CALL	LD$OP2
	CALL	FPMULT
	JMP	EXIT0

XDIV:
	CALL	LD$OP2
	CALL	FPDIV
	JMP	EXIT0

XFTOA:
	CALL	LD$OP1
	CALL	FTOA
	RET

LD$OP1:
	LHLD	arg3
	XCHG
	LXI	H,FPACC-1
	MVI	M,0
	INX	H
	MVI	C,5
	CALL	MOVE
	RET

LD$OP2:
	CALL	LD$OP1
	LHLD	arg4
	XCHG
	LXI	H,FPOP-1
	MVI	M,0
	INX	H
	MVI	C,5
	CALL	MOVE
	RET

ST$ACC:
	LHLD	arg2
	LXI	D,FPACC
	MVI	C,5
	CALL	MOVE
	RET

FPNORM:
	LDA	FPACC+3		;Get MS byte of FPACC
	STA	SIGN		;Save SIGN byte of FPACC
	ANA	A		;If number is positive
	JP	NZERO$TEST	;.. go test for zero
	LXI	H,FPACC-1	;Load addr of FPACC (+ xtra byte)
	MVI	C,5		;Load precision register
	CALL	NEGATE		;Negate FPACC

NZERO$TEST:
	LXI	H,FPACC-1
	MVI	C,5
	CALL	ZERO$TEST	;If FPACC not zero
	JNZ	NOTZERO		;.. go normalize
	STA	FPACCX		;make sure exponent is zero
	RET

NOTZERO:
	LXI	H,FPACC-1
	MVI	C,5
	CALL	SHIFTL		;shift FPACC left
	LXI	H,FPACCX
	DCR	M		;subtract 1 from FPACC exponent
	LDA	FPACC+3		;get MS byte of FPACC
	ANA	A		;if high order bit not no
	JP	NOTZERO		;.. go do again

;compensate for last shift

	LXI	H,FPACCX
	INR	M
	DCX	H
	MVI	C,5
	CALL	SHIFTR
	LDA	SIGN		;fetch original sign
	RAL			;shift sign bit into carry
	RNC			;exit if orig # was positive
	LXI	H,FPACC-1
	MVI	C,5
	CALL	NEGATE		;2's complement FPACC
	RET			;Exit FPNORM
FPADD:
	LXI	H,FPACC
	MVI	C,4
	CALL	ZERO$TEST		;if FPACC not = zero
	JNZ	TEST$FPOP		;.. go test FPOP for zero
	LXI	H,FPACC
	LXI	D,FPOP
	MVI	C,5
	CALL	MOVE		;Move FPOP to  FPACC
	RET			;Exit FPADD
TEST$FPOP:
	LXI	H,FPOP
	MVI	C,4
	CALL	ZERO$TEST	;if FPOP = 0
	RZ			;.. exit FPADD
	LDA	FPACCX
	LXI	H,FPOPX
	SUB	M		;if exponents are equal
	JZ	ADD$SETUP	;.. go to add setup
	JP	RANGE$TEST	;if diff of exp >=0,goto range test
	CMA
	INR	A		;ABS of difference

RANGE$TEST:
	CPI	32		;if diff < 32
	JM	ALGN$OPRNDS	;.. we can go align the operands
	LXI	H,FPACCX
	LDA	FPOPX
	SUB	M		;if exp of FPACC > exp of FPOP
	RM			;.. exit FPADD
	LXI	D,FPOP
	LXI	H,FPACC
	MVI	C,5
	CALL	MOVE		;move FPOP to FPACC
	RET			;Exit FPADD

ALGN$OPRNDS:
	LDA	FPACCX
	LXI	H,FPOPX
	SUB	M		;subt exponents
	MOV	B,A		;save difference of exponents
	JM	SHFT$FPACC	;if diff neg, go shift FPACC

ALGN$FPOP:
	LXI	H,FPOPX
	CALL	SHFT$LOOP	;shift FPOP & increment exponent
	DCR	B		;Decrement diff register
	JNZ	ALGN$FPOP	;loop until exponents are equal
	JMP	ADD$SETUP	;go to add setup

SHFT$FPACC:
	LXI	H,FPACCX
	CALL	SHFT$LOOP	;shift FPACC & increment exponent
	INR	B		;increment difference register
	JNZ	SHFT$FPACC	;loop until exponents are equal

ADD$SETUP:
	XRA	A
	STA	FPACC-1
	STA	FPOP-1
	LXI	H,FPACCX
	CALL	SHFT$LOOP	;shift FPACC right
	LXI	H,FPOPX
	CALL	SHFT$LOOP	;shift FPOP right
	LXI	H,FPACC-1
	LXI	D,FPOP-1
	MVI	C,5
	CALL	ADDER		;add FPOP to FPACC
	CALL	FPNORM		;normalize result
	RET			;exit FPADD

SHFT$LOOP:
	INR	M		;increment exponent
	DCX	H		;decrement ptr
	MVI	C,4
	MOV	A,M		;get MS byte
	ANA	A		;if negative number
	JM	SHFT$MINUS	;.. goto negative shift
	CALL	SHIFTR		;shift mantissa
	RET

SHFT$MINUS:
	STC			;set carry
	CALL	SHFTR		;shift mantissa progatating sign
	RET			;exit

FPSUB:
	LXI	H,FPACC
	MVI	C,4
	CALL	NEGATE
	JMP	FPADD

FPMULT:
	CALL	SIGNJOB		;process the signs
	LXI	H,WORK
	MVI	C,8
	CALL	ZERO$MEMORY	;WORK := 0 (partial product)
	LXI	H,FPACCX
	LDA	FPOPX
	ADD	M
	INR	A		;compensate for algolrithm
	MOV	M,A		;add FPOP exp to FPACC exponent
	LXI	H,FPACC-4
	MVI	C,4
	CALL	ZERO$MEMORY	;clear multiplicand extra bytes
	LXI	H,BITS
	MVI	M,31

MULT$LOOP:
	LXI	H,FPOP+3
	MVI	C,4
	CALL	SHIFTR		;shift multiplier right
	CC	ADD$MULTIPLICAND ;add multiplicand if carry
	LXI	H,WORK+7
	MVI	C,8
	CALL	SHIFTR		;shift partial product right
	LXI	H,BITS
	DCR	M		;decrement BITS counter
	JNZ	MULT$LOOP	;if not zero, do again
	LXI	H,WORK+7
	MVI	C,8
	CALL	SHIFTR		;shift once more for rounding
	LXI	H,WORK+3
	MOV	A,M
	RAL			;fetch 32th bit
	ANA	A		;if it is a 1
	CM	ROUND$IT	;.. round the result
	LXI	D,WORK+3
	LXI	H,FPACC-1
	MVI	C,5
EXMLDV:
	CALL	MOVE
	LDA	SIGN		;fetch SIGN and save it on the stack
	PUSH	PSW
	CALL	FPNORM
	POP	PSW
	ANA	A
	RP
	LXI	H,FPACC
	MVI	C,4
	CALL	NEGATE
	RET

ADD$MULTIPLICAND:
	LXI	H,WORK
	LXI	D,FPACC-4
	MVI	C,8
	CALL	ADDER
	RET
ROUND$IT:
	MVI	A,40H
	ADD	M
	MVI	C,4
RND$LOOP:
	MOV	M,A
	INX	H
	MVI	A,0
	ADC	M
	DCR	C
	JNZ	RND$LOOP
	MOV	M,A
	RET
FPDIV:
	LXI	H,FPOP
	MVI	C,4
	CALL	ZERO$TEST
	JNZ	DIV$SIGN
	LXI	H,FPACC
	MVI	C,5
	CALL	ZERO$MEMORY
	RET

DIV$SIGN:
	CALL	SIGNJOB
	LXI	H,WORK
	MVI	C,12
	CALL	ZERO$MEMORY
	MVI	A,31
	STA	BITS
	LXI	H,FPACCX
	LDA	FPOPX
	MOV	B,A
	MOV	A,M
	SUB	B
	INR	A
	MOV	M,A
DIVIDE:
	CALL	SETSUB		;WORK2 := dividend - divisor
	JM	NOGO		;if minus, go put 0 in quotient
	LXI	H,FPACC
	LXI	D,WORK2
	MVI	C,4
	CALL	MOVE		;move subt results to dividend
	STC
	JMP	QUOROT

NOGO:
	ANA	A
QUOROT:
	LXI	H,WORK+4
	MVI	C,4
	CALL	SHFTL		;Insert carry flag into quotient
	LXI	H,FPACC
	MVI	C,4
	CALL	SHFTL		;shift dividend left
	LXI	H,BITS
	DCR	M		;decrement BITS counter
	JNZ	DIVIDE		;loop until BITS = zero
	CALL	SETSUB		;1 more time for rounding
	JM	DVEXIT		;if 24th bit = 0, goto exit
	LXI	H,WORK+4
	LXI	D,ONE
	MVI	C,4
	CALL	ADDER
	LXI	H,WORK+7
	MOV	A,M
	ANA	A
	JP	DVEXIT
	MVI	C,4
	CALL	SHIFTR
	LXI	H,FPACCX
	INR	M
DVEXIT:
	LXI	H,FPACC
	LXI	D,WORK+4
	MVI	C,4
	JMP	EXMLDV

SETSUB:
	LXI	D,FPACC
	LXI	H,WORK2
	MVI	C,4
	CALL	MOVE		;move dividend to work2
	LXI	H,WORK2
	LXI	D,FPOP
	MVI	C,4
	CALL	SUBBER		;subtract divisor from work2
	LDA	WORK2+3
	ANA	A
	RET

FTOA:
	LHLD	arg2
	SHLD	ASCII$PTR
	MVI	M,' '
	LDA	FPACC+3
	ANA	A
	JP	BYSIGN
	MVI	M,'-'
	LXI	H,FPACC
	MVI	C,4
	CALL	NEGATE
BYSIGN:
	LHLD	ASCII$PTR
	INX	H
	MVI	M,'0'
	INX	H
	MVI	M,'.'
	INX	H
	SHLD	ASCII$PTR
	XRA	A
	STA	EXP
	LXI	H,FPACC
	MVI	C,4
	CALL	ZERO$TEST
	JNZ	SU$FTOA
	MVI	C,7
	LHLD	ASCII$PTR
ZERO$LOOP:
	MVI	M,'0'
	INX	H
	DCR	C
	JNZ	ZERO$LOOP
	SHLD	ASCII$PTR
	JMP	EXPOUT
SU$FTOA:
	LXI	H,FPACCX
	DCR	M
DECEXT:
	JP	DECEXD
	MVI	A,4
	ADD	M
	JP	DECOUT
	CALL	FPX10
DECREP:
	LXI	H,FPACCX
	MOV	A,M
	ANA	A
	JMP	DECEXT

DECEXD:
	CALL	FPD10
	JMP	DECREP	

DECOUT:
	LXI	H,FPACC
	LXI	D,ADJ
	MVI	C,4
	CALL	ADDER
	LXI	H,OUTAREA
	LXI	D,FPACC
	MVI	C,4
	CALL	MOVE
	LXI	H,OUTAREA+4
	MVI	M,0
	LXI	H,OUTAREA
	MVI	C,4
	CALL	SHIFTL
	CALL	OUTX10
COMPEN:
	LXI	H,FPACCX
	INR	M
	JZ	OUTDIG
	LXI	H,OUTAREA+4
	MVI	C,5
	CALL	SHIFTR
	JMP	COMPEN
OUTDIG:
	MVI	A,7
	STA	DIGCNT
	LXI	H,OUTAREA+4
	MOV	A,M
	ANA	A
	JZ	ZERODG
OUTDGS:
	LXI	H,OUTAREA+4
	MVI	A,'0'
	ADD	M
	LHLD	ASCII$PTR
	MOV	M,A
	INX	H
	SHLD	ASCII$PTR
DECRDG:
	LXI	H,DIGCNT
	DCR	M
	JZ	EXPOUT
	CALL	OUTX10
	JMP	OUTDGS

ZERODG:
	LXI	H,EXP
	DCR	M
	LXI	H,OUTAREA
	MVI	C,5
	CALL	ZERO$TEST
	JNZ	DECRDG
	XRA	A
	STA	DIGCNT
	JMP	DECRDG

OUTX10:
	XRA	A
	STA	OUTAREA+4
	LXI	H,WORK
	LXI	D,OUTAREA
	MVI	C,5
	CALL	MOVE
	LXI	H,OUTAREA
	MVI	C,5
	CALL	SHIFTL
	LXI	H,OUTAREA
	MVI	C,5
	CALL	SHIFTL
	LXI	D,WORK
	LXI	H,OUTAREA
	MVI	C,5
	CALL	ADDER
	LXI	H,OUTAREA
	MVI	C,5
	CALL	SHIFTL
	RET
EXPOUT:
	LHLD	ASCII$PTR
	MVI	M,'E'
	INX	H
	LDA	EXP
	ANA	A
	JP	EXPOT
	CMA
	INR	A
	STA	EXP
	MVI	M,'-'
	INX	H
	LDA	EXP
EXPOT:
	MVI	C,0
EXPLOOP:
	SUI	10
	JM	TOMUCH
	STA	EXP
	INR	C
	JMP	EXPLOOP

TOMUCH:
	MVI	A,'0'
	ADD	C
	MOV	M,A
	INX	H
	LDA	EXP
	ADI	'0'
	MOV	M,A
	INX	H
	MVI	M,0
	RET
FPX10:
	LXI	H,FPOP
	LXI	D,TEN
	MVI	C,5
	CALL	MOVE
	CALL	FPMULT
	LXI	H,EXP
	DCR	M
	RET

FPD10:
	LXI	H,FPOP
	LXI	D,ONE$TENTH
	MVI	C,5
	CALL	MOVE
	CALL	FPMULT
	LXI	H,EXP
	INR	M
	RET

NEGATE:
	STC			;CARRY forces an add of 1
NEGAT$LOOP:
	MOV	A,M		;fetch byte
	CMA			;complement it
	ACI	0		;make it two's complement
	MOV	M,A		;store the result
	INX	H		;bump ptr
	DCR	C		;decrement precision register
	JNZ	NEGAT$LOOP	;if not done, go do it again
	RET			;Return to caller

ZERO$TEST:
	XRA	A		;clear A
	ORA	M		;'OR' A with next byte
	INX	H		;bump ptr
	DCR	C		;decrement precision register
	JNZ	ZERO$TEST+1	;loop until done
	ANA	A		;set flags
	RET

SHIFTL:
	ANA	A		;clear CARRY
SHFTL:
	MOV	A,M		;get next byte
	RAL			;shift it left
	MOV	M,A		;store result
	INX	H		;bump ptr
	DCR	C		;decrement precision register
	JNZ	SHFTL		;loop until done
	RET

SHIFTR:
	ANA	A
SHFTR:
	MOV	A,M
	RAR
	MOV	M,A
	DCX	H
	DCR	C
	JNZ	SHFTR
	RET

ADDER:
	ANA	A
ADD$LOOP:
	LDAX	D
	ADC	M
	MOV	M,A
	INX	D
	INX	H
	DCR	C
	JNZ	ADD$LOOP
	RET

SUBBER:
	ANA	A
	XCHG
SUB$LOOP:
	LDAX	D
	SBB	M
	STAX	D
	INX	D
	INX	H
	DCR	C
	JNZ	SUB$LOOP
	XCHG
	RET

ZERO$MEMORY:
	MVI	M,0
	INX	H
	DCR	C
	JNZ	ZERO$MEMORY
	RET

MOVE:
	LDAX	D
	MOV	M,A
	INX	D
	INX	H
	DCR	C
	JNZ	MOVE
	RET

SIGNJOB:
	LDA	FPACC+3
	STA	SIGN
	ANA	A
	JP	CKFPOP
	LXI	H,FPACC
	MVI	C,4
	CALL	NEGATE
CKFPOP:
	LXI	H,SIGN
	LDA	FPOP+3
	XRA	M
	MOV	M,A
	LDA	FPOP+3
	ANA	A
	RP
	LXI	H,FPOP
	MVI	C,4
	CALL	NEGATE
	RET

	DS	4
FPACC:	DS	4
FPACCX:	DS	1
	DS	4
FPOP:	DS	4
FPOPX:	DS	1
SIGN:	DS	1
WORK:	DS	8
WORK2:	DS	4
BITS:	DS	1
ASCII$PTR:	DS	2
EXP:	DS	1
OUTAREA:	DS	5
DIGCNT:	DS	1
ONE$TENTH:	DB	66H,66H,66H,66H,0FDH
TEN:	DB	0,0,0,50H,4
ADJ:	DB	5,0,0,0
ONE:	DB	80H,0,0,0

	ENDFUNC

